package crew.blt.handsound.client.webgl.element;

import java.util.HashSet;
import java.util.Set;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.VerticalPanel;

import crew.blt.handsound.client.webgl.HasJSONStringValue;
import crew.blt.handsound.client.webgl.SceneJSUtil;
import crew.blt.handsound.client.webgl.property.Property;
import crew.gwt.client.widgets.ClassicLabel;

/**
 * An Element represent a SceneJs node would be accessible on the SceneJS.
 * Element has an set of property which supported by SceneJS
 * @author blt
 *
 */
public abstract class Element extends Composite implements ValueChangeHandler , HasJSONStringValue{ // PropertyChangeHandler, 
	
	private final String id;
	private final Set<Property<?,?>> properties = new HashSet<Property<?,?>>();
	private final VerticalPanel panelProperties = new VerticalPanel();
	private final VerticalPanel panelChild = new VerticalPanel();
	private ElementChangeHandler handler = null;
	
	public Element(String id) {
		this.id = id;
		
		// Element Panel
		DisclosurePanel pl = new DisclosurePanel();
		pl.setAnimationEnabled(true);
		pl.setHeader(new ClassicLabel(id + " ("+getElementType()+")"));
		pl.setContent(panelProperties);
		
		// Child panel
		DisclosurePanel childs = new DisclosurePanel();
		childs.setAnimationEnabled(true);
		childs.setHeader(new ClassicLabel("View Childs :"));
		childs.setContent(panelChild);
		panelProperties.add(childs);
		
		initWidget(pl);
	}

	public String getId() {
		return id;
	}
	
	public ElementChangeHandler getHandler() {
		return handler;
	}

	public void setHandler(ElementChangeHandler handler) {
		this.handler = handler;
	}

	/**
	 * Add a property into the widget's list of this element.
	 * @param <T>
	 * @param property
	 * @return
	 */
	public <T> boolean addProperty(Property<T, ?> property,boolean readJSONValue) throws JavaScriptException {
		boolean addSuccess = properties.add(property);
		if(addSuccess){
			property.addValueChangeHandler(this);
			panelProperties.insert(property,0);
			// Read the value in the JSON graph then write it into the property object.
			if(readJSONValue){
				T sceneJSValue = SceneJSUtil.readPropertyValue(this.getId(), property);
				property.setValue(sceneJSValue,false);
			} 
			// Init the property value
			else {
				property.setValueByDefault(false);
			}
				
		}
		return addSuccess;
	}

	/**
	 * Handle event where event.getSource() is a Property.
	 * This method fire the value's property change into the SceneJS's JSON tree 
	 * In case of ElementChangehandler onto this element, it's will be called.
	 */
	@Override
	public void onValueChange(ValueChangeEvent event) {
		Object o = event.getSource();
		if(o instanceof Property<?, ?>){
			Property<?, ?> property = (Property<?, ?>)o;
			SceneJSUtil.writePropertyValue(getId(),property.getPropertyName(),property.getValueAsString());
			if( handler != null ){
				handler.onElementChange(this, property);
			}
		}
	}
	

	/**
	 * @return the Element type. ( translate , rotate , box, etc )
	 */
	public abstract String getElementType();
	
	@Override
	public String getJSONString() {
		String propertiesString = SceneJSUtil.createJSONPropertiesString(properties);
		return "{ type: " + getElementType() + "," +
				" id: "+getId()+", " + propertiesString +
				" }";
	}
	
	public void addChild(Element element){
		SceneJSUtil.insertElementIntoNode(element.getJSONString(), getId());
	}

	public Set<Property<?, ?>> getProperties() {
		return properties;
	}
}
